feat: embedded dashboard v1 (Connect + Memory) - #60
Merged
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Ship the hosted dashboard with the engine itself: a React SPA (loomem-dashboard/, Vite + Tailwind, Loomem DS v2 tokens, self-hosted fonts) compiled into the loomem-server binary via rust-embed and served at / on every instance identically — hosted fleet, loomem.ai, localhost. Why in the binary: single-user Loomem's deployment story is one self-contained file; a separate frontend process or directory would break that on the fleet and for self-hosters. Screens (v1): Connect (real MCP endpoint from the page origin, unlock token reveal/copy, per-client recipes for Claude Desktop / Claude Code / ChatGPT / Cursor parameterized with the real endpoint+token, live status probing /v1/status), Memory (server-paginated list, substring + semantic search, inline edit/delete over the existing /api/memories/:id, version history, related entities), Settings. Login is token-unlock over the existing auth_middleware + /v1/whoami — no new auth surface. Server side: new handlers/dashboard.rs (GET /api/dashboard/memory list, GET /v1/memory-chain/:id via loomem_core::contradiction) reusing chunk_to_memory_item and resolve_scope; scope.rs source_for returns to production use (its dashboard consumer is back). spa.rs serves the embedded dist/ with an SPA fallback that keeps API-shaped paths as honest 404s. CI gains a dashboard job; release.yml and Dockerfile build the SPA before cargo. With an unbuilt dist/ the server still compiles and / answers an honest 404. Deviations from the brief (one-liners): unused donor components (Avatar, FilterChip, SegmentedControl, InlineEditField, KeyRevealModal, useKeyboardShortcuts, useStoredSort, RealityBenchTrend) were not ported — no consumer in v1, zero dead code in a public repo; IdentityBanner collapsed to stream+total (no user identity in single-user); stats tiles skipped (/v1/stats counts searches/stores, not memories). Critical file rationale: main.rs changes are additive — two route registrations in the declarative table (CC=1 preserved) plus a fallback registered outside the auth layer; no existing route, middleware, or control flow changed. Verified by router_builds_without_panic, 6 new dashboard handler tests, 4 spa tests, and cargo test --workspace. Signed-off-by: Łukasz Gumowski <lukasz.gumowski@gmail.com>
vvooki-sys
force-pushed
the
feat/dashboard-v1
branch
from
July 10, 2026 20:19
f6685a6 to
08f8360
Compare
`page` is parsed straight from the query string, so a request like `?page=18446744073709551615&per_page=200` reached the offset multiplication in dashboard_memory_handler — an overflow panic in debug builds, a wrapped (and thus wrong, earlier) page in release. Use saturating_mul so an oversized page clamps to usize::MAX, which .skip() turns into a harmless empty page; normal pagination is unchanged (page-1 can't underflow, page is .max(1)). Addresses Greptile P1 review comment on #60. Signed-off-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The hosted dashboard, shipped inside the engine: a React SPA (
loomem-dashboard/, Vite + Tailwind, Loomem DS v2) embedded into theloomem-serverbinary viarust-embedand served at/on every instance identically — hosted fleet, loomem.ai, localhost. One binary, no separate frontend process.Screens (v1):
/v1/status(no mock anywhere)./api/memories/:id, version history (/v1/memory-chain/:id), related entities in-panel.auth_middleware+GET /v1/whoami. No new auth surface; in local passthrough mode (no token configured) the gate opens straight through.Server:
handlers/dashboard.rs(new):GET /api/dashboard/memory(list; reuseschunk_to_memory_itemso rows are byte-identical withGET /api/memories/:id) andGET /v1/memory-chain/:id(thin wrapper over the existingloomem_core::contradiction::get_memory_chain).spa.rs(new): rust-embed fallback with client-side-routing support; API-shaped paths stay honest 404s; hashed assets are cache-immutable; an unbuiltdist/compiles fine and/answers an honest 404.scope.rs:source_forreturns to production use (its dashboard consumer is back).main.rs: additive only — two routes in the declarative table (CC=1 preserved) + the SPA fallback registered outside the auth layer so the login screen loads without a token.Build/infra:
rust-embedadded to workspace deps (rationale in Cargo.toml), CI gains aDashboardjob (npm ci/lint/test/build),release.ymlandDockerfilebuild the SPA before cargo. Fonts are self-hosted via@fontsource-variable(Fraunces full variant carries the opsz axis) — deterministic rendering offline and in headless environments, no Google Fonts request.Verification
cargo fmt --check+cargo clippy --workspace -- -D warnings— green (pre-commit hook).cargo test --workspace— 303 passed; the only 2 failures are the pre-existingac6_3_*MCP tool-count tests already failing onmain(they count 14/15 tools; untouched by this PR — CI runs--liband stays green).vitest52/52,eslintclean,vite buildclean.LOOMEM_AUTH_TOKENset: bad token refused → good token unlocks; Connect shows the real endpoint/token, recipes parameterized, live probe goes green; the copied recipe's credentials complete a real MCPinitializehandshake against/mcp(and 401 without them); Memory lists seeded data, server-side search filters, history loads, edit persists via PUT, delete removes the row; a mid-session 401 drops back to the login screen.assets/dashboard-v1-screensbranch:Deviations from the brief
/v1/statscounts searches/stores, not memories/entities; an honest tile needs a small aggregate endpoint (v1.1 candidate alongside Graph).Out of scope (v1.1+)
Graph 3D (spec in the mockup), stats tiles, httpOnly-cookie token storage.
Greptile Summary
This PR embeds a React dashboard into
loomem-serverand wires it into the existing authenticated API surface. The main changes are:rust-embedwith client-route fallback and API-shaped 404 handling.Confidence Score: 5/5
This PR appears safe to merge based on the reviewed paths.
The changed server routes remain behind existing auth except for static SPA serving. The SPA fallback preserves API 404 behavior. The reviewed dashboard API and UI flows are internally consistent. No new blocking functional or security issues were identified.
No files require special attention.
What T-Rex did
Important Files Changed
/v1/whoamiand global 401 handling.distbefore Rust compilation.Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant Browser as Dashboard SPA participant Fallback as spa_fallback participant Auth as Auth Middleware participant API as Dashboard/API Handlers participant Store as RocksDB/Indexes Browser->>Fallback: GET /, /memory, assets Fallback-->>Browser: index.html or embedded asset Browser->>Auth: GET /v1/whoami with Bearer token Auth->>API: authenticated request API-->>Browser: user context Browser->>Auth: "GET /api/dashboard/memory?page&q&filters" Auth->>API: dashboard_memory_handler API->>Store: resolve scope, scan chunks/entities Store-->>API: scoped memory rows API-->>Browser: paginated memory list Browser->>Auth: PUT/DELETE /api/memories/:id or GET /v1/memory-chain/:id Auth->>API: existing admin/dashboard handlers API->>Store: update/delete/read version chain API-->>Browser: mutation result or history%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant Browser as Dashboard SPA participant Fallback as spa_fallback participant Auth as Auth Middleware participant API as Dashboard/API Handlers participant Store as RocksDB/Indexes Browser->>Fallback: GET /, /memory, assets Fallback-->>Browser: index.html or embedded asset Browser->>Auth: GET /v1/whoami with Bearer token Auth->>API: authenticated request API-->>Browser: user context Browser->>Auth: "GET /api/dashboard/memory?page&q&filters" Auth->>API: dashboard_memory_handler API->>Store: resolve scope, scan chunks/entities Store-->>API: scoped memory rows API-->>Browser: paginated memory list Browser->>Auth: PUT/DELETE /api/memories/:id or GET /v1/memory-chain/:id Auth->>API: existing admin/dashboard handlers API->>Store: update/delete/read version chain API-->>Browser: mutation result or historyReviews (4): Last reviewed commit: "fix(dashboard): saturate pagination offs..." | Re-trigger Greptile